package com.ssm.socket;


import com.qiniu.util.Hex;
import com.ssm.listen.IniListener;
import com.ssm.service.BaoqiServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Socket服务器类
 */
@Component
@Slf4j
public class SocketModel implements ApplicationListener<ApplicationContextEvent> {


    private ServerSocket serverSocket;

    //询问地址指令
    private static final String ask = "000100000006ff03009A0001";
    //map存储设备id和指令
    private Map<Integer, String> zmap = new HashMap<>();
    //ini文件路径
    private static final File iniFile = new File("/mnt/cinding/baoqi/cfg.ini");

    //引入ini文件监听
    @Autowired
    private IniListener iniListener;
    //引入曝气业务类
    @Autowired
    private BaoqiServiceImpl baoqiService;


    public void setZl(Map<Integer, String> zmap) {
        this.zmap = zmap;
    }



    //启动socket服务
    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        //通讯服务端绑定10005端口
        try {
            serverSocket = new ServerSocket(10003);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        //创建线程池,以便开启多线程通讯,避免端口阻塞
        ExecutorService executorService = Executors.newFixedThreadPool(100);

        //保持一直循环,使程序一直监听
        while (true) {

            //监听端口
            Socket server;
            try {
                server = serverSocket.accept();
            } catch (IOException e) {
                e.printStackTrace();
                log.error("发生监听io异常,详情: {}", e.getMessage());
                return;
            }


            //创建线程,线程处理通讯相关逻辑
            Runnable runnable = () -> {

                    try {
                        log.info("等待远程连接，端口号为：" + serverSocket.getLocalPort() + "...");
                        log.info("远程主机地址：" + server.getRemoteSocketAddress());
                        //从监听socket中获取输入流
                        BufferedInputStream in = new BufferedInputStream(server.getInputStream());
                        BufferedOutputStream out = new BufferedOutputStream(server.getOutputStream());

                        //声明比特数组
                        byte[] bytes = new byte[1024];
                        int len;
                        //设置计数器,每个设备交互3秒后,歇6秒
                        long p = 0;
                            //将输入流读取到比特数组
                            while ((len = in.read(bytes)) != -1) {
                                   //设置执行io读取开始时间戳
                                    long start = System.currentTimeMillis();
                                    //调用读取ini时间策略
                                    setZl(iniListener.onApplicationEvent(iniFile));

                                    //剔除比特数组中多余的部分
                                    byte[] newBytes = Arrays.copyOf(bytes, len);
                                    //将字节数组转成16进制字符
                                    String content = Hex.encodeHexString(newBytes);

                                    //将比特数组转为字符串
                                    //String content = new String(newBytes);

                                    log.info("接收到报文为: {}", content);
                                    //将报文截取最后四位
                                    String hexEq = StringUtils.substring(content, content.length() - 4);
                                    //转成10进制
                                    int equipId = Integer.parseInt(hexEq, 16);

                                    if (zmap.containsKey(equipId)) {

                                        //根据对应设备编号发送指令
                                        out.write(Hex.decodeHex(zmap.get(equipId).toCharArray()));
                                        log.info("发送设备id为{}, 状态为{}", equipId, zmap.get(equipId));
                                        out.flush();
                                        //传送曝气报文给suzhou_baoqi服务
                                        baoqiService.sendToBqServer(equipId, zmap.get(equipId));
                                        //设置执行io读取结束时间戳
                                        long end = System.currentTimeMillis();
                                        //计算时间差,累加时间差
                                        p = p + end - start;
                                        //如果时间差累计超过5秒,则歇60秒
                                        if (p >= 1000) {
                                            p = 0;
                                           Thread.sleep(60000);
                                        }
                                        continue;
                                    }
                                    //发送询问指令
                                    out.write(Hex.decodeHex(ask.toCharArray()));
                                    log.info("发送问询指令为: {}", ask);
                                    out.flush();
                                    //设置执行io读取结束时间戳
                                    long end = System.currentTimeMillis();
                                    //计算时间差,累加时间差
                                     p = p + end - start;
                                    //如果时间差累计超过5秒,则歇60秒
                                    if (p >= 1000) {
                                        p = 0;
                                        Thread.sleep(60000);
                                    }
                                }

                        //关流
                        in.close();
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        log.error("发生io异常, 详情: {}", e.getMessage());
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        log.error("发生运行异常: {}", e.getMessage());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            };
            //将线程加入线程池
            executorService.submit(runnable);

        }
    }

}
